package com.tengju.user.application.login;

import com.tengju.user.application.login.dto.PwdChangeDTO;
import com.tengju.user.application.login.dto.RegisterDTO;
import com.tengju.user.application.shared.ApplicationException;
import com.tengju.user.application.shared.ApplicationExceptionCode;
import com.tengju.user.application.shared.Constant;
import com.tengju.user.application.shared.JwtUtil;
import com.tengju.user.domain.model.login.*;
import com.tengju.user.domain.model.sms.SmsVerifyCodeRepository;
import com.tengju.user.domain.model.user.Staff;
import com.tengju.user.domain.model.user.StaffId;
import com.tengju.user.domain.model.user.UserAccountRepository;
import com.tengju.user.domain.model.user.UserStaffRepository;
import com.tengju.user.domain.shared.IdObject;
import com.tengju.user.domain.shared.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;


@Slf4j
@Service
public class UserAccountService {

    @Value("${user.mobile.areaCode}")
    String areaCode;

    @Resource
    ApplicationContext applicationContext;

    private final UserAccountRepository userAccountRepository;
    private final UserStaffRepository userStaffRepository;
    private final SmsVerifyCodeRepository smsVerifyCodeRepository;

    @Autowired
    public UserAccountService(UserAccountRepository userAccountRepository, UserStaffRepository userStaffRepository, SmsVerifyCodeRepository smsVerifyCodeRepository) {
        this.userAccountRepository = userAccountRepository;
        this.userStaffRepository = userStaffRepository;
        this.smsVerifyCodeRepository = smsVerifyCodeRepository;
    }

    @Transactional(value = "dealerTransactionManager", rollbackFor = Exception.class)
    public LoginResult login(LoginForm loginForm, UserAccount userAccount) {

        LoginStrategy loginService = (LoginStrategy) applicationContext
                .getBean(loginForm.getLoginType().getStrategyName());

        //校验账号信息
        LoginResult loginResult = loginService.login(loginForm, userAccount);

        if (userAccount == null) {
            //注册
            loginResult = loginService.registerAndLogin(loginForm);
        }

        if (loginResult == null) {
            throw ApplicationException.error(ApplicationExceptionCode.LOGIN_FAIL);
        }

        //生成token
        String tokenStr = Constant.LOGIN_TOKEN_PRE + JwtUtil.sign(loginResult.getUsername(),
                loginResult.getPassword());
        loginResult.addToken(tokenStr);

        userAccountRepository.saveToken(tokenStr, JsonUtil.toJSONString(loginResult));


        return loginResult;

    }

    public Boolean logout(LogoutInfo logoutInfo, IdObject idObject, UserType userType) {

        if (logoutInfo.validId(idObject, userType)) {
            userAccountRepository.delToken(logoutInfo.getToken());
        } else {
            throw ApplicationException
                    .error(ApplicationExceptionCode.USER_TOKEN_MISMATCH, logoutInfo);
        }
        return true;

    }

    public boolean checkMobile(String areaCode, String mobile) {

        UserAccount userAccount = userAccountRepository
                .queryUserAccount(areaCode + mobile, UserType.USER);

        if (userAccount == null) {
            throw ApplicationException.error(ApplicationExceptionCode.USER_NOT_REGISTER);
        }

        return true;
    }

    public UserAccount pwdChange(PwdChangeDTO pwdChangeDTO) {

        String pwdChangeToken = smsVerifyCodeRepository.getPwdChangeToken(
                UserType.fromVal(pwdChangeDTO.getUserType()), pwdChangeDTO.getAreaCode(),
                pwdChangeDTO.getMobile());

        if (!StringUtils.isEmpty(pwdChangeToken) &&
                pwdChangeToken.equals(pwdChangeDTO.getPwdChangeToken())) {

            UserAccount userAccount = userAccountRepository.queryUserAccount(
                    pwdChangeDTO.getAreaCode() + pwdChangeDTO.getMobile(),
                    UserType.fromVal(pwdChangeDTO.getUserType()));

            if (userAccount == null) {
                throw ApplicationException.error(ApplicationExceptionCode.USER_NOT_REGISTER);
            }
            userAccount.changePwd(pwdChangeDTO.getNewPassword());
            //变更密码
            userAccountRepository.changePwd(userAccount);
            //删除密码变更token
            userAccountRepository.delPwdChangeToken(
                    UserType.fromVal(pwdChangeDTO.getUserType()), pwdChangeDTO.getAreaCode(),
                    pwdChangeDTO.getMobile());

            return userAccount;

        } else {
            throw ApplicationException.error(ApplicationExceptionCode.PWD_CHANGE_ERROR);
        }

    }

    public String getAreaCode() {
        return areaCode;
    }

    @Transactional(value = "dealerTransactionManager", rollbackFor = Exception.class)
    public void addManageUser(RegisterDTO registerDTO) {
        Staff staff = new Staff(null,
                registerDTO.getRealName(),
                null,
                "admin",
                LocalDateTime.now(),
                "admin",
                LocalDateTime.now());
        StaffId staffId = userStaffRepository.store(staff);

        UserAccount userAccount = new UserAccount(null,
                registerDTO.getUsername(),
                DigestUtils.md5DigestAsHex(registerDTO.getPassword().getBytes()),
                UserType.fromVal(registerDTO.getUserType()),
                null,
                staffId,
                null,
                null,
                null);
        userAccountRepository.store(userAccount);
    }


    public int countTotal(UserType userType) {
        return userAccountRepository.countTotal(userType);
    }

    public int countNew(UserType userType,LocalDateTime from, LocalDateTime to) {
        return userAccountRepository.countNew(userType,from,to);
    }
}
